<?php
/**
 *  Generic Abstract Model: per-row table access
 *
 *  Similar to Active Record pattern, in which the record is implemented in Item,
 *      and the data access in Action.
 *  Implement the common methods of selecting items and executing actions
 *
 */
namespace Yz\modules\Generic;

abstract class Model_Abstract
    extends \Yz_Model_Abstract
    implements \Yz_Model_Interface
{

    /**
     *  @var Yz_Action_Fetch_Interface
     */
    protected $fetchAction;
    /**
     *  @var Yz_Action_Update_Interface
     */
    protected $updateAction;
    /**
     *  @var Yz_Action_Create_Interface
     */
    protected $createAction;
    /**
     *  @var Yz_Action_Delete_Interface
     */
    protected $deleteAction;

    /**
     *  a name of item class (Yz_Item_Interface)
     *  @var string
     */
    protected $itemName = "Default";



    /************************************
     *          Public methods          *
     ************************************/


    /**
     *  get item class
     *  @return string
     */
    public function fetchItemConfig(\Yz_Item_Interface $item)
    {
        debug();
        return $this->module
            ->getConfig()
            ->fetchSectionWithInherit('Item.'.$item->shortClassName())
            ;
    }


    /**
     *  create item template
     *  @return Yz_Item_Interface
     */
    public function selectItem($name=null)
    {
        debug($name);
        if (null === $name) {
            $name = $this->itemName;
        }
        $class = $this->identifyItemClass($name);
        return new $class($this);
    }



    /************************************
     *       Protected methods          *
     ************************************/



    /**
     *  identify item class
     *  @return string
     */
    protected function identifyItemClass($name)
    {
        debug();
        if (/* ### DISABLED ### */false && property_exists($this,"item_{$name}_class")) {
            debug("Using item class name from property: '{$name}'");
            $class = $this->{"item_{$name}_class"};
        } else {
            $class = $this->module->resolveLibrary('Item', $name);
            if (false === $class) {
                throw new Yz_Exception_LibraryNotFound("Failed to create object");
            }
        }
        assert('is_subclass_of($class,"Yz_Item_Interface")');
        return $class;
    }


    /**
     *  create action
     *  @return Yz_Action_Interface
     */
    protected function selectAction( $actionName )
    {
        debug();
        $class = $this->module->resolveLibrary('Action', $actionName);
        assert('is_subclass_of($class,"Yz_Action_Interface")');

        // TODO take class name from property of $this
        $action = new $class($this->describeSource());

        return $action;
    }


    /**
     *  get a hash with item's source names (SQL table aliases) as keys and SQL table names as values
     *
     *  The default implementation returns one source entry
     *  with the table name built from the current module's namespace
     *  and with the alias 't0'
     *  @return Yz_Hash
     *  @todo use config 'sources'
     */
    protected function describeSource()
    {
        debug();
        $out = array();
        $defaultSource = $this->getDefaultSource();
        $cfg = $this->getConfig()->fetchSection("source");
        if (null === $cfg) {
            $out['t0'] = $defaultSource;
        } else {
            foreach ($cfg as $alias => $data) {
                if (is_string($data)) {
                    $src = str_replace('~', $defaultSource, $data);
                } else {
                    $src = $data;
                    if ($src->offsetExists('name')) {
                        $src['name'] = str_replace('~', $defaultSource, $src['name']);
                    } else {
                        // ERROR, name must be present
                    }
                }
                $out[$alias] = $src;
            }
        }
        return new \Yz_Hash($out);
    }

    protected function getDefaultSource()
    {
        debug();
        return strtolower($this->module->getName());
    }


}
